﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using Common;
using WcfService.DataContracts;

namespace WcfClient
{
    public class Wp7OrtProxy : IRemoteLookupService
    {
        private Iwp7ortClient _wpLocalHostClient;
        public event Action<Dictionary<TagInfo, List<PointInfo>>> PointsFound;
        public event Action<TagInfo> ImageAdded;

        public event Action<bool> HBWaitngForResponse;
        private int _hbWaitingCount;
        private bool _hbWaiting;

        public event Action<bool> ServerOnline;
        private bool _serverOnline;

        public Wp7OrtProxy(IpeRemoteLookup ipeRemoteLookup)
        {
            string host;

            switch (ipeRemoteLookup)
            {
                case IpeRemoteLookup.LocalHost:
                    host = "http://eee/wp7ort.svc";
                    break;

                case IpeRemoteLookup.RemoteShared:
                    host = "http://wp7ort.apporista.com/wp7ort.svc";
                    break;

                default:
                    return;
            }

            _wpLocalHostClient = new Iwp7ortClient("BasicHttpBinding_Iwp7ort", host);
            _wpLocalHostClient.CloseCompleted += CloseCompleted;
            _wpLocalHostClient.AddImageCompleted += AddImageCompleted;
            _wpLocalHostClient.LookupPointsCompleted += LookupPointsCompleted;
            _wpLocalHostClient.LookupOverlayCompleted += LookupOverlayCompleted;
        }

        void LookupOverlayCompleted(object sender, LookupOverlayCompletedEventArgs e)
        {
            RaiseHBWaitingForResponse(false);

            throw new NotImplementedException();
        }

        private void RaiseHBWaitingForResponse(bool waiting)
        {
            int count = waiting ? Interlocked.Increment(ref _hbWaitingCount) : Interlocked.Decrement(ref _hbWaitingCount);

            Action<bool> evtHbWaiting = HBWaitngForResponse;
            if (evtHbWaiting == null)
                return;

            bool waitingState = count > 0;

            if (_hbWaiting == waitingState)
                return;

            _hbWaiting = waitingState;
            evtHbWaiting(waiting);
        }

        private void RaiseServerOnline(bool isOnline)
        {
            if (isOnline == _serverOnline)
                return;

            _serverOnline = isOnline;

            var evtServerOnline = ServerOnline;
            if (evtServerOnline == null)
                return;

            evtServerOnline(isOnline);
        }


        void LookupPointsCompleted(object sender, LookupPointsCompletedEventArgs e)
        {
            RaiseHBWaitingForResponse(false);

            if (e.Error != null)
            {
                RaiseServerOnline(false);
                return;
            }

            if (e.Result != null && e.Result.BestFoundObject != null)
                RaisePointsFound(e.Result.BestFoundObject, e.Result.AllFoundObjects);

            RaiseServerOnline(true);
        }

        private void RaisePointsFound(FoundObjectInfo bestFoundObject, FoundObjectInfo[] allFoundObjects)
        {
            Action<Dictionary<TagInfo, List<PointInfo>>> pointsFound = PointsFound;
            if (pointsFound == null)
                return;

            Dictionary<TagInfo, List<PointInfo>> pointsMatched = new Dictionary<TagInfo, List<PointInfo>>();

            foreach (var foundObject in allFoundObjects)
                CopyFoundObjectsToIPs(foundObject, pointsMatched);

            pointsFound(pointsMatched);
        }

        private static void CopyFoundObjectsToIPs(FoundObjectInfo foundObject, Dictionary<TagInfo, List<PointInfo>> pointsMatched)
        {
            foreach (var pointInfo in foundObject.FoundPoints)
            {
                if (!pointsMatched.ContainsKey(foundObject.TagInfo))
                    pointsMatched[foundObject.TagInfo] = new List<PointInfo>();

                pointsMatched[foundObject.TagInfo].Add(pointInfo);
            }
        }

        private void AddImageCompleted(object sender, AsyncCompletedEventArgs e)
        {
            RaiseHBWaitingForResponse(false);

            if (e.Error != null)
            {
                RaiseServerOnline(false);
                return;
            }

            var evtImageAdded = ImageAdded;
            if (evtImageAdded == null)
                return;

            evtImageAdded(((ImageData)e.UserState).TagInfo);

            RaiseServerOnline(true);
        }

        private void CloseCompleted(object sender, AsyncCompletedEventArgs e)
        {
            _wpLocalHostClient = null;
        }

        public void Dispose()
        {
            if (_wpLocalHostClient != null)
            {
                _wpLocalHostClient.CloseAsync();
            }
        }

        public void AddImage(int width, int height, TagInfo tagInfo, ref int[] learntArgbFrame, ref byte[] learntYFrame)
        {
            var imageData = new ImageData()
                               {
                                   TagInfo = tagInfo,
                                   ZipCompressedArgbData = CompressionHelpers.Compress(CompressionHelpers.BtyeArrayFromIntArray(learntArgbFrame), tagInfo.TagName),
                                   ZipCompressedYData = CompressionHelpers.Compress(learntYFrame, tagInfo.TagName),
                                   Width = width,
                                   Height = height
                               };
            RaiseHBWaitingForResponse(true);
            _wpLocalHostClient.AddImageAsync(imageData, imageData);
        }

        public void LookupPoints(List<PointInfo> points)
        {
            if (_wpLocalHostClient == null)
                return;

            if (_hbWaitingCount > 0)
                return;

            LookupPointsRequest scannedPoints = new LookupPointsRequest() { PointsInfo = points.ToArray() };

            RaiseHBWaitingForResponse(true);
            _wpLocalHostClient.LookupPointsAsync(scannedPoints);
        }
    }
}
